package show.marvelous.domain.chessgame.aggregate.entity;

import cn.hutool.core.util.IdUtil;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Optional;
import lombok.Data;
import lombok.NonNull;
import org.jilt.Builder;
import org.jilt.BuilderStyle;
import show.marvelous.domain.chessgame.constans.AirPlanColorEnum;
import show.marvelous.domain.chessgame.constans.MoveTypeEnum;
import show.marvelous.domain.chessgame.constans.PieceCodeEnum;
import show.marvelous.domain.chessgame.constans.PieceEnum;
import show.marvelous.domain.chessgame.constans.TakeOffStrategyEnum;

/**
 * The type Chessboard location.
 * @author Kris 2021/8/17
 */
@Data
public class Position{

    public final static Integer CEILING_INDEX = -1;
    public final static Integer TAKE_OFF_INDEX = 0;
    public final static Integer END_INDEX = 57;
    /** 终点直道转角位置 */
    public static final int END_POINT_CORNER = 50;
    public static final int FIRST_LONG_FLY_POSITION = 14;
    public static final int SECOND_LONG_FLY_POSITION = 18;
    private final AirPlanColorEnum color;
    private final Integer index;
    private final Collection<PieceEnum> pieces;

    @Builder(style = BuilderStyle.TYPE_SAFE)
    public Position(AirPlanColorEnum color, Integer index){
        this.color = color;
        this.index = index;
        this.pieces = new ArrayList<>(4);
    }

    public Integer toPieceColorIndex(@NonNull AirPlanColorEnum otherColor){
        if(otherColor == color){
            return index;
        }
        int colorOffset = 0;
        for(AirPlanColorEnum targetColor = this.color; otherColor != targetColor; colorOffset++){
            targetColor = targetColor.nextColor();
        }
        return (index + 52 - 13 * colorOffset) % 52;
    }

    public boolean isSamePosition(AirPlanColorEnum color, Integer index){
        return toPieceColorIndex(color).equals(index);
    }

    public Optional<MoveEvent> moveOut(AirPlanColorEnum color, PieceCodeEnum code, int step,
            @NonNull TakeOffStrategyEnum takeOffStrategy){
        Optional<PieceEnum> pieceOptional = findPiece(color, code);
        if(!pieceOptional.isPresent()){
            throw new IllegalStateException("当前位置找不到棋子：" + color.getColorName() + code.name());
        }
        PieceEnum piece = pieceOptional.get();
        // 如果还没起飞.那么需要根据起飞策略判断能否起飞
        if(this.index.equals(CEILING_INDEX)){
            if(takeOffStrategy.canTakeOff(step)){
                MoveEvent moveEvent = MoveEventBuilder.moveEvent().id(IdUtil.getSnowflake().nextId())
                        .type(MoveTypeEnum.JUMP).piece(piece).positionColor(color).positionIndex(TAKE_OFF_INDEX)
                        .build();
                losePiece(piece);
                return Optional.of(moveEvent);
            }
        }else{
            int nextIndex = toPieceColorIndex(piece.getColor()) + step;
            MoveEvent moveEvent = MoveEventBuilder.moveEvent().id(IdUtil.getSnowflake().nextId())
                    .type(MoveTypeEnum.MOVE).piece(piece).positionColor(piece.getColor()).positionIndex(nextIndex)
                    .build();
            losePiece(piece);
            return Optional.of(moveEvent);
        }
        return Optional.empty();
    }

    public Collection<MoveEvent> handleMoveEvent(MoveEvent moveEvent){
        Collection<MoveEvent> moveEvents = new ArrayList<>(5);
        PieceEnum piece = moveEvent.getPiece();
        Integer curIndex = toPieceColorIndex(piece.getColor());
        // 1. 如果相同颜色并且可以跳,那么产生一条跳跃事件
        if(piece.getColor() == this.color && moveEvent.getType() == MoveTypeEnum.MOVE){
            // 去到第 50 格之后，虽然颜色相同但是不能跳跃
            if(curIndex < END_POINT_CORNER){
                int nextIndex;
                if(curIndex == FIRST_LONG_FLY_POSITION){
                    nextIndex = 30;
                    MoveEvent flyEvent = MoveEventBuilder.moveEvent().id(IdUtil.getSnowflake().nextId())
                            .type(MoveTypeEnum.FLY).piece(piece).positionColor(piece.getColor().nextColor().nextColor())
                            .positionIndex(53).build();
                    moveEvents.add(flyEvent);
                }else if(curIndex == SECOND_LONG_FLY_POSITION){
                    nextIndex = 34;
                    MoveEvent flyEvent = MoveEventBuilder.moveEvent().id(IdUtil.getSnowflake().nextId())
                            .type(MoveTypeEnum.FLY).piece(piece).positionColor(piece.getColor().nextColor().nextColor())
                            .positionIndex(53).build();
                    moveEvents.add(flyEvent);
                }else{
                    nextIndex = curIndex + 4;
                }
                MoveEvent jumpEvent = MoveEventBuilder.moveEvent().id(IdUtil.getSnowflake().nextId())
                        .type(MoveTypeEnum.JUMP).piece(piece).positionColor(piece.getColor()).positionIndex(nextIndex)
                        .build();
                moveEvents.add(jumpEvent);
            }
        }
        // 2. 对其他颜色的棋子产生一条回退事件
        for(PieceEnum otherPiece : pieces){
            if(otherPiece.getColor().equals(piece.getColor())){
                continue;
            }
            MoveEvent backEvent = MoveEventBuilder.moveEvent().id(IdUtil.getSnowflake().nextId())
                    .type(MoveTypeEnum.BACK).piece(otherPiece).positionColor(otherPiece.getColor())
                    .positionIndex(CEILING_INDEX).build();
            moveEvents.add(backEvent);
        }
        return moveEvents;
    }

    public void gotPiece(PieceEnum piece){
        this.pieces.add(piece);
    }

    public void losePiece(PieceEnum piece){
        this.pieces.remove(piece);
    }

    private Optional<PieceEnum> findPiece(AirPlanColorEnum color, PieceCodeEnum code){
        return pieces.stream().filter(p -> p.getColor().equals(color) && p.getCode().equals(code)).findFirst();
    }

    public boolean containPiece(AirPlanColorEnum color, PieceCodeEnum code){
        return findPiece(color, code).isPresent();
    }

    public Iterator<PieceEnum> getPieces(){
        return pieces.iterator();
    }

    public AirPlanColorEnum getColor(){
        return color;
    }
}
